<template>
  <div ref="uploadImageRef" class="upload-image">
    <el-upload
      ref="uploadRef"
      v-model:file-list="modelFileListCustomer"
      :auto-upload="false"
      :accept="uploadAccept"
      :limit="1"
      list-type="picture-card"
      :on-preview="showImageViewer"
      :on-remove="removeFileHandle"
      :class="[uploadClass]"
      @change="changeFileHandle"
    >
      <slot name="default">
        <el-icon><Plus /></el-icon>
      </slot>
    </el-upload>
    <el-image-viewer
      v-if="imageViewerState" :url-list="imageViewerUrlList"
      :initial-index="0"
      @close="hideImageViewer"
    />
  </div>
</template>

<script lang="ts" setup>
import { computed, ref, withDefaults } from 'vue'
import { Plus } from '@element-plus/icons-vue'
import { ElUpload, UploadUserFile } from 'element-plus'
import { dataset } from '@yk/components/upload/accept'
import { ObjectUtil } from '@utils/object-util'

const props = withDefaults(defineProps<{
  modelValue?: UploadUserFile,
  uploadClass?: string,
  accept?: string,
  width?: string,
  height?: string,
  maxSize?: number,
  onError?(error: Error, file: UploadUserFile): void
}>(), {
  width: '148px',
  height: '148px',
  uploadClass: '',
  maxSize: Infinity,
  accept: 'png,jpg,jpeg'
})

const emits = defineEmits(['update:modelValue', 'change', 'remove'])

const uploadImageRef = ref<HTMLDivElement>()
const uploadRef = ref<InstanceType<typeof ElUpload>>()

onMounted(() => {
  uploadImageRef.value.style.setProperty('--upload-image-size-w', props.width)
  uploadImageRef.value.style.setProperty('--upload-image-size-h', props.height)
})

// region TODO 数据同步
const modelFileListCustomer = computed({
  get (): UploadUserFile[] {
    return props.modelValue ? [props.modelValue] : []
  },
  set (val: UploadUserFile[]) {
    emits('update:modelValue', val[0])
    imageViewerUrlList.value = val[0] ? [val[0].url] : []
  }
})
function removeFileHandle () {
  emits('update:modelValue', undefined)
  emits('remove', undefined)
}
function changeFileHandle (file: UploadUserFile) {
  let error = null
  if (!verifyFileType(file.raw)) {
    error = Error('文件类型应为' + props.accept)
  } else if (file.size > props.maxSize) {
    const unitVal = ObjectUtil.matchUnit(props.maxSize, 'bit', 'maxCarry', { carryNumber: 1024, fractionDigits: 2 })
    error = Error('文件大小不能超过' + unitVal.value + unitVal.unit)
  }
  if (error) {
    uploadRef.value.handleRemove(file.raw)
    if (props.onError) props.onError(error, file)
  } else {
    emits('change', file)
  }
}
// endregion

// region TODO 图片放大
const imageViewerUrlList = shallowRef([])
const imageViewerState = ref(false)
function showImageViewer () {
  imageViewerState.value = true
}
function hideImageViewer () {
  imageViewerState.value = false
}
// endregion

// region TODO 文件类型限制
const uploadAccept = computed(() => props.accept.split(',').map(key => dataset[key]).join(','))
// 检查类型是否合法，true合法，false不合法
function verifyFileType (file: File): boolean {
  return props.accept.split(',').includes(file.name.replace(/^.*\.(\w*)$/, '$1'))
}
// endregion

</script>

<style lang="scss" scoped>
.upload-image {
  width: var(--upload-image-size-w);
  height: var(--upload-image-size-h);
  overflow: hidden;
}
::v-deep(.el-upload-list--picture-card) {
  width: var(--upload-image-size-w);
  height: var(--upload-image-size-h);
  .el-upload-list__item {
    margin: 0;
    width: var(--upload-image-size-w);
    height: var(--upload-image-size-h);
  }
  .el-upload--picture-card {
    width: var(--upload-image-size-w);
    height: var(--upload-image-size-h);
    overflow: hidden;
  }
}
</style>
